#ifndef PORTABLE_FEEDBACK_VECTOR_H_
#define PORTABLE_FEEDBACK_VECTOR_H_

#include <vector>

#include "src/flags/flags.h"
#include "src/pbc/portable-globals.h"

namespace portable {

inline bool is_strict(LanguageMode language_mode) {
  return language_mode != LanguageMode::kSloppy;
}

using PropertyValue = std::pair<void*, String>;
struct PropertyHasher {
  size_t operator()(const PropertyValue& v) const {
    size_t seed = std::hash<void*>()(v.first);
    size_t rhs = StringHasher()(v.second);
    seed ^= (rhs + 0x9e3779b9 + (seed << 6) + (seed >> 2));
    return seed;
  }
};

struct PropertyEqual {
  size_t operator()(const PropertyValue& lhs, const PropertyValue& rhs) const {
    return lhs.first == rhs.first && StringEqual()(lhs.second, rhs.second);
  }
};

class FeedbackVectorSpec {
 public:
  explicit FeedbackVectorSpec() : slot_count(0) {}

  uint32_t AddSlot() { return slot_count++; }

  int GetCachedCreateClosureSlot(void* function) {
    if (cached_closure_slots_.count(function)) {
      return cached_closure_slots_[function];
    }
    return cached_closure_slots_[function] = AddSlot();
  }

  uint32_t GetCachedLoadICSlot(void* obj, String property) {
    if (!v8::internal::v8_flags.ignition_share_named_property_feedback) {
      return AddSlot();
    }
    auto& cached_property_slot = cached_property_slot_[0];
    PropertyValue key = std::make_pair(obj, property);
    if (cached_property_slot.count(key)) {
      return cached_property_slot[key];
    }
    return cached_property_slot[key] = AddSlot();
  }

  uint32_t GetCachedLoadGlobalICSlot(TypeofMode typeof_mode, String name) {
    auto& cached_global_slot =
        cached_global_slot_[typeof_mode == TypeofMode::kInside ? 0 : 1];
    if (cached_global_slot.count(name)) {
      return cached_global_slot[name];
    }
    return cached_global_slot[name] = AddSlot();
  }

  uint32_t GetCachedStoreICSlot(LanguageMode language_mode, void* obj,
                                String property) {
    if (!v8::internal::v8_flags.ignition_share_named_property_feedback) {
      return AddSlot();
    }
    auto& cached_property_slot =
        cached_property_slot_[is_strict(language_mode) ? 1 : 2];
    PropertyValue key = std::make_pair(obj, property);
    if (cached_property_slot.count(key)) {
      return cached_property_slot[key];
    }
    return cached_property_slot[key] = AddSlot();
  }

  uint32_t GetCachedStoreGlobalICSlot(LanguageMode language_mode, String name) {
    auto& cached_global_slot =
        cached_global_slot_[is_strict(language_mode) ? 2 : 3];
    if (cached_global_slot.count(name)) {
      return cached_global_slot[name];
    }
    return cached_global_slot[name] = AddSlot();
  }

 private:
  std::unordered_map<String, uint32_t, StringHasher, StringEqual>
      cached_global_slot_[4];
  std::unordered_map<PropertyValue, uint32_t, PropertyHasher, PropertyEqual>
      cached_property_slot_[3];
  std::unordered_map<void*, uint32_t> cached_closure_slots_;
  uint32_t slot_count;
};

}  // namespace portable

#endif  //  PORTABLE_FEEDBACK_VECTOR_H_
